﻿using Org.BouncyCastle.Utilities.Encoders;
using RuoVea.SM.SMExtend;
using System;
using System.Security.Cryptography;
using System.Text;

namespace RuoVea.SM;

/// <summary>
/// SM4国密对称加密工具类
/// 提供ECB和CBC模式的加密解密功能，支持静态密钥和动态密钥
/// </summary>
public class SM4Crypt
{
    private const int KeySize = 16; // SM4密钥长度(16字节)
    private const int IvSize = 16;  // SM4初始向量长度(16字节)

    /// <summary>
    /// 默认加密密钥(16字节)
    /// </summary>
    public string DefaultSecretKey { get; set; } = "1814546261730461";

    /// <summary>
    /// 默认偏移量(16字节)
    /// </summary>
    public string DefaultIV { get; set; } = "0000000000000000";

    /// <summary>
    /// 是否使用16进制字符串格式的密钥和IV
    /// </summary>
    public bool HexString { get; set; } = false;

    #region ECB模式加密解密

    /// <summary>
    /// ECB模式加密(使用默认密钥)
    /// </summary>
    public string EncryptECB(string plainText)
    {
        return EncryptECB(plainText, DefaultSecretKey);
    }

    /// <summary>
    /// ECB模式加密(动态密钥)
    /// </summary>
    public string EncryptECB(string plainText, string secretKey)
    {
        ValidateInput(plainText, nameof(plainText));
        ValidateKey(secretKey, nameof(secretKey));

        var keyBytes = HexString ? Hex.Decode(secretKey) : Encoding.UTF8.GetBytes(secretKey);

        var sm4 = new SM4();
        var ctx = new SM4_Context
        {
            isPadding = true,
            mode = SM4.SM4_ENCRYPT
        };

        sm4.Sm4_setkey_enc(ctx, keyBytes);
        byte[] encrypted = sm4.Sm4_crypt_ecb(ctx, Encoding.UTF8.GetBytes(plainText));

        return Hex.ToHexString(encrypted);
    }

    /// <summary>
    /// ECB模式解密(使用默认密钥)
    /// </summary>
    public string DecryptECB(string cipherText)
    {
        return DecryptECB(cipherText, DefaultSecretKey);
    }

    /// <summary>
    /// ECB模式解密(动态密钥)
    /// </summary>
    public string DecryptECB(string cipherText, string secretKey)
    {
        ValidateInput(cipherText, nameof(cipherText));
        ValidateKey(secretKey, nameof(secretKey));

        var keyBytes = HexString ? Hex.Decode(secretKey) : Encoding.UTF8.GetBytes(secretKey);

        var sm4 = new SM4();
        var ctx = new SM4_Context
        {
            isPadding = true,
            mode = SM4.SM4_DECRYPT
        };

        sm4.Sm4_setkey_dec(ctx, keyBytes);
        byte[] decrypted = sm4.Sm4_crypt_ecb(ctx, Hex.Decode(cipherText));

        return Encoding.UTF8.GetString(decrypted);
    }

    #endregion

    #region CBC模式加密解密

    /// <summary>
    /// CBC模式加密(使用默认密钥和IV)
    /// </summary>
    public string EncryptCBC(string plainText)
    {
        return EncryptCBC(plainText, DefaultSecretKey, DefaultIV);
    }

    /// <summary>
    /// CBC模式加密(动态密钥和IV)
    /// </summary>
    public string EncryptCBC(string plainText, string secretKey, string iv)
    {
        ValidateInput(plainText, nameof(plainText));
        ValidateKey(secretKey, nameof(secretKey));
        ValidateIV(iv, nameof(iv));

        var keyBytes = HexString ? Hex.Decode(secretKey) : Encoding.UTF8.GetBytes(secretKey);
        var ivBytes = HexString ? Hex.Decode(iv) : Encoding.UTF8.GetBytes(iv);

        var sm4 = new SM4();
        var ctx = new SM4_Context
        {
            isPadding = true,
            mode = SM4.SM4_ENCRYPT
        };

        sm4.Sm4_setkey_enc(ctx, keyBytes);
        byte[] encrypted = sm4.Sm4_crypt_cbc(ctx, ivBytes, Encoding.UTF8.GetBytes(plainText));

        return Hex.ToHexString(encrypted);
    }

    /// <summary>
    /// CBC模式解密(使用默认密钥和IV)
    /// </summary>
    public string DecryptCBC(string cipherText)
    {
        return DecryptCBC(cipherText, DefaultSecretKey, DefaultIV);
    }

    /// <summary>
    /// CBC模式解密(动态密钥和IV)
    /// </summary>
    public string DecryptCBC(string cipherText, string secretKey, string iv)
    {
        ValidateInput(cipherText, nameof(cipherText));
        ValidateKey(secretKey, nameof(secretKey));
        ValidateIV(iv, nameof(iv));

        var keyBytes = HexString ? Hex.Decode(secretKey) : Encoding.UTF8.GetBytes(secretKey);
        var ivBytes = HexString ? Hex.Decode(iv) : Encoding.UTF8.GetBytes(iv);

        var sm4 = new SM4();
        var ctx = new SM4_Context
        {
            isPadding = true,
            mode = SM4.SM4_DECRYPT
        };

        sm4.Sm4_setkey_dec(ctx, keyBytes);
        byte[] decrypted = sm4.Sm4_crypt_cbc(ctx, ivBytes, Hex.Decode(cipherText));

        return Encoding.UTF8.GetString(decrypted);
    }

    #endregion

    #region 静态方法

    /// <summary>
    /// 静态方法:ECB加密
    /// </summary>
    public static string EncryptECB(string plainText, string secretKey, bool hexString = false)
    {
        return new SM4Crypt { HexString = hexString }.EncryptECB(plainText, secretKey);
    }

    /// <summary>
    /// 静态方法:ECB解密
    /// </summary>
    public static string DecryptECB(string cipherText, string secretKey, bool hexString = false)
    {
        return new SM4Crypt { HexString = hexString }.DecryptECB(cipherText, secretKey);
    }

    /// <summary>
    /// 静态方法:CBC加密
    /// </summary>
    public static string EncryptCBC(string plainText, string secretKey, string iv, bool hexString = false)
    {
        return new SM4Crypt { HexString = hexString }.EncryptCBC(plainText, secretKey, iv);
    }

    /// <summary>
    /// 静态方法:CBC解密
    /// </summary>
    public static string DecryptCBC(string cipherText, string secretKey, string iv, bool hexString = false)
    {
        return new SM4Crypt { HexString = hexString }.DecryptCBC(cipherText, secretKey, iv);
    }

    #endregion

    #region Helper Methods

    private void ValidateKey(string key, string paramName)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException(paramName);

        int expectedLength = HexString ? KeySize * 2 : KeySize;
        if (key.Length != expectedLength)
            throw new ArgumentException($"{paramName} must be {expectedLength} characters long");
    }

    private void ValidateIV(string iv, string paramName)
    {
        if (string.IsNullOrEmpty(iv))
            throw new ArgumentNullException(paramName);

        int expectedLength = HexString ? IvSize * 2 : IvSize;
        if (iv.Length != expectedLength)
            throw new ArgumentException($"{paramName} must be {expectedLength} characters long");
    }

    private void ValidateInput(string input, string paramName)
    {
        if (string.IsNullOrEmpty(input))
            throw new ArgumentNullException(paramName);
    }

    #endregion

    #region 密钥生成方法

    /// <summary>
    /// 生成随机SM4密钥(实例方法)
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机密钥</returns>
    public string GenerateKey(bool hexFormat = false)
    {
        byte[] keyBytes = new byte[KeySize];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(keyBytes);
        }

        if (hexFormat)
        {
            return Hex.ToHexString(keyBytes).ToUpper();
        }
        return Encoding.ASCII.GetString(keyBytes);
    }

    /// <summary>
    /// 生成随机IV(实例方法)
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机IV</returns>
    public string GenerateIV(bool hexFormat = false)
    {
        byte[] ivBytes = new byte[IvSize];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(ivBytes);
        }

        if (hexFormat)
        {
            return Hex.ToHexString(ivBytes).ToUpper();
        }
        return Encoding.ASCII.GetString(ivBytes);
    }

    /// <summary>
    /// 静态方法：生成随机SM4密钥
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机密钥</returns>
    public static string GenerateSM4Key(bool hexFormat = false)
    {
        var sm4 = new SM4Crypt();
        return sm4.GenerateKey(hexFormat);
    }

    /// <summary>
    /// 静态方法：生成随机IV
    /// </summary>
    /// <param name="hexFormat">是否返回16进制格式</param>
    /// <returns>16字节的随机IV</returns>
    public static string GenerateSM4IV(bool hexFormat = false)
    {
        var sm4 = new SM4Crypt();
        return sm4.GenerateIV(hexFormat);
    }

    #endregion
}